﻿using AutoMapper;
using ElasticSearch.BLL.DAL;
using ElasticSearch.BLL.Model;
using ElasticSearch.DAL;
using PlainElastic.Net;
using PlainElastic.Net.Mappings;
using PlainElastic.Net.Queries;
using PlainElastic.Net.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ElasticSearch.BLL.Helper
{
    public class ElasticSearchHelper
    {
        public static readonly ElasticSearchHelper Intance = new ElasticSearchHelper();
        private static ElasticConnection Client;
        private ElasticSearchHelper()
        {
            var node = new Uri("http://localhost:9200");
            Client = new ElasticConnection("localhost", 9200);
        }
        /// <summary>
        /// 数据索引
        /// </summary>       
        /// <param name="indexName">索引名称</param>
        /// <param name="indexType">索引类型</param>
        /// <param name="id">索引文档id，不能重复,如果重复则覆盖原先的</param>
        /// <param name="jsonDocument">要索引的文档,json格式</param>
        /// <returns></returns>
        public IndexResult Index(string indexName, string indexType, string id, string jsonDocument)
        {

            var serializer = new JsonNetSerializer();
            string cmd = new IndexCommand(indexName, indexType, id);
            OperationResult result = Client.Put(cmd, jsonDocument);

            var indexResult = serializer.ToIndexResult(result.Result);
            return indexResult;
        }
        public IndexResult Index(string indexName, string indexType, string id, object document)
        {
            var serializer = new JsonNetSerializer();
            var jsonDocument = serializer.Serialize(document);
            return Index(indexName, indexType, id, jsonDocument);
        }

        /// <summary>
        /// ElasticeSearch查询ptypecollection
        /// </summary>
        /// <param name="indexName">索引名称</param>
        /// <param name="indexType">索引类型</param>
        /// <param name="key">查询key</param>
        /// <param name="keyValue">查询keyValue</param>
        /// <param name="from">分页开始位置</param>
        /// <param name="size">分页大小</param>
        /// <returns></returns>
        public static Elastic<ptypecollectionDto> Query(string indexName,string indexType,string key,string keyValue,int from,int size)
        {
            string cmd = new SearchCommand(indexName, indexType);
            string query = new QueryBuilder<ptypecollectionDto>()
                .Query(b =>
                            b.Bool(m =>
                                //并且关系
                                m.Must(t =>
                                   //分词的最小单位或关系查询
                                   //t.QueryString(t1 => t1.DefaultField(key).Query(keyValue).Analyzer("ik_smart"))
                                   t//.Term(p=>p.Field(key).Value("1"))
                                   .QueryString(t1 => t1.DefaultField(key).Query(keyValue).Analyzer("ik_smart"))
                                     )
                                  )
                                )
                 .Sort(p=>p.Field("_score", SortDirection.desc))
                 .From(from)
                 .Size(size)
                 .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName(key).Order(HighlightOrder.score),
                             f => f.FieldName("_all")
                             )
                     )
                 .Build();
            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<ptypecollectionDto>(result);
            Elastic<ptypecollectionDto> datalist = new Elastic<ptypecollectionDto>();
            datalist = new Elastic<ptypecollectionDto>()
            {
                Took = list.took,
                TimeOut = list.timed_out,
                Shards = new Shards()
                {
                    Total = list._shards.total,
                    Failed = list._shards.failed,
                    Successful = list._shards.successful
                },
                Hits = new Hits<ptypecollectionDto>()
                {
                    MaxScore = list.hits.max_score,
                    Total = list.hits.total,
                    Data = list.hits.hits.Select(p => new ptypecollectionDto()
                    {
                        _index = p._index,
                        _type = p._type,
                        _id = p._id,
                        _score = p._score,
                        ID = p._source.ID,
                        Classify = p._source.Classify,
                        FullName = p._source.FullName,
                        Name = p._source.Name,
                        E_Name = p._source.E_Name,
                        Standard = p._source.Standard,
                        Type = p._source.Type,
                        PermitNo = p._source.PermitNo,
                        PermitNoPeriod = p._source.PermitNoPeriod,
                        CompanyName = p._source.CompanyName,
                        BarCode = p._source.BarCode,
                        Unit1 = p._source.Unit1,
                        Unit2 = p._source.Unit2,
                        Unit3 = p._source.Unit3,
                        UnitRate1 = p._source.UnitRate1,
                        Unitrate2 = p._source.Unitrate2,
                        EditDate = p._source.EditDate,
                        AddDate = p._source.AddDate,
                        AdverseReaction = p._source.AdverseReaction,
                        Address = p._source.Address,
                        ChildDrugUse = p._source.ChildDrugUse,
                        Component = p._source.Component,
                        CreateDate = p._source.CreateDate,
                        DrugReaction = p._source.DrugReaction,
                        InnerID = p._source.InnerID,
                        GMPCode = p._source.GMPCode,
                        GMPPeriod = p._source.GMPPeriod,
                        LiverKidneyDysfunction = p._source.LiverKidneyDysfunction,
                        OlderDrugUse = p._source.OlderDrugUse,
                        PregnacyDrugUse = p._source.PregnacyDrugUse,
                        ProhibitShareDrug = p._source.ProhibitShareDrug,
                        UsefulLifeDay = p._source.UsefulLifeDay,
                        ValDateType = p._source.ValDateType,
                        Indiaction = p._source.Indiaction,
                        IndicationsFunction = p._source.IndicationsFunction,
                        Ingredient = p._source.Ingredient,
                        IsCustom = p._source.IsCustom,
                        IsUse = p._source.IsUse,
                        Note = p._source.Note,
                        PharmacistPrompt = p._source.PharmacistPrompt,
                        Pharmacokinetics = p._source.Pharmacokinetics,
                        PharmacologyToxicology = p._source.PharmacologyToxicology,
                        PictureInfo = p._source.PictureInfo,
                        StorageCondition = p._source.StorageCondition,
                        Taboo = p._source.Taboo,
                        TypeStatus = p._source.TypeStatus,
                        Useage = p._source.Useage,
                        UseCount = p._source.UseCount,
                        UsefulLifeMonth = p._source.UsefulLifeMonth
                    }).ToList()
                }
            };
            return datalist;
        }

        public static Elastic<ptypecollectionDto> Querys(string indexName, string indexType, string key, string keyValue, int from, int size)
        {
            string cmd = new SearchCommand(indexName, indexType);
            string[] keys = key.Split(',');
            string query = new QueryBuilder<ptypecollectionDto>()
                .Query(b =>
                            b.Bool(m =>
                                //并且关系
                                m.Must(t =>
                                   //分词的最小单位或关系查询
                                   //t.QueryString(t1 => t1.DefaultField(key).Query(keyValue).Analyzer("ik_smart"))
                                   t.QueryString(t1 => t1.Fields(keys).Query(keyValue).Analyzer("ik_smart"))
                                     )
                                  )
                                )
                 //分页
                 .Sort(p => p.Field("_score", SortDirection.desc))
                 .From(from)
                 .Size(size)
                 .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName(key).Order(HighlightOrder.score),
                             f => f.FieldName("_all")
                             )
                     )
                 .Build();
            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<ptypecollectionDto>(result);
            Elastic<ptypecollectionDto> datalist = new Elastic<ptypecollectionDto>();
            datalist = new Elastic<ptypecollectionDto>()
            {
                Took = list.took,
                TimeOut = list.timed_out,
                Shards = new Shards()
                {
                    Total = list._shards.total,
                    Failed = list._shards.failed,
                    Successful = list._shards.successful
                },
                Hits = new Hits<ptypecollectionDto>()
                {
                    MaxScore = list.hits.max_score,
                    Total = list.hits.total,
                    Data = list.hits.hits.Select(p => new ptypecollectionDto()
                    {
                        _index = p._index,
                        _type = p._type,
                        _id = p._id,
                        _score = p._score,
                        ID = p._source.ID,
                        Classify = p._source.Classify,
                        FullName = p._source.FullName,
                        Name = p._source.Name,
                        E_Name = p._source.E_Name,
                        Standard = p._source.Standard,
                        Type = p._source.Type,
                        PermitNo = p._source.PermitNo,
                        PermitNoPeriod = p._source.PermitNoPeriod,
                        CompanyName = p._source.CompanyName,
                        BarCode = p._source.BarCode,
                        Unit1 = p._source.Unit1,
                        Unit2 = p._source.Unit2,
                        Unit3 = p._source.Unit3,
                        UnitRate1 = p._source.UnitRate1,
                        Unitrate2 = p._source.Unitrate2,
                        EditDate = p._source.EditDate,
                        AddDate = p._source.AddDate,
                        AdverseReaction = p._source.AdverseReaction,
                        Address = p._source.Address,
                        ChildDrugUse = p._source.ChildDrugUse,
                        Component = p._source.Component,
                        CreateDate = p._source.CreateDate,
                        DrugReaction = p._source.DrugReaction,
                        InnerID = p._source.InnerID,
                        GMPCode = p._source.GMPCode,
                        GMPPeriod = p._source.GMPPeriod,
                        LiverKidneyDysfunction = p._source.LiverKidneyDysfunction,
                        OlderDrugUse = p._source.OlderDrugUse,
                        PregnacyDrugUse = p._source.PregnacyDrugUse,
                        ProhibitShareDrug = p._source.ProhibitShareDrug,
                        UsefulLifeDay = p._source.UsefulLifeDay,
                        ValDateType = p._source.ValDateType,
                        Indiaction = p._source.Indiaction,
                        IndicationsFunction = p._source.IndicationsFunction,
                        Ingredient = p._source.Ingredient,
                        IsCustom = p._source.IsCustom,
                        IsUse = p._source.IsUse,
                        Note = p._source.Note,
                        PharmacistPrompt = p._source.PharmacistPrompt,
                        Pharmacokinetics = p._source.Pharmacokinetics,
                        PharmacologyToxicology = p._source.PharmacologyToxicology,
                        PictureInfo = p._source.PictureInfo,
                        StorageCondition = p._source.StorageCondition,
                        Taboo = p._source.Taboo,
                        TypeStatus = p._source.TypeStatus,
                        Useage = p._source.Useage,
                        UseCount = p._source.UseCount,
                        UsefulLifeMonth = p._source.UsefulLifeMonth
                    }).ToList()
                }
            };
            return datalist;
        }

        public static Elastic<ptypecollectionDto> QueryNoKey(string indexName, string indexType, string keyValue, int from, int size)
        {
            string cmd = new SearchCommand(indexName, indexType);
            string query = new QueryBuilder<ptypecollectionDto>()
                .Query(b =>
                            b.Bool(m =>
                                //并且关系
                                m.Must(t =>
                                   //分词的最小单位或关系查询
                                   //t.QueryString(t1 => t1.DefaultField(key).Query(keyValue).Analyzer("ik_smart"))
                                   t.QueryString(t1 => t1.Query(keyValue).Analyzer("ik_smart"))
                                     )
                                  )
                                )
                 //分页
                 .Sort(p => p.Field("_score", SortDirection.desc))
                 .From(from)
                 .Size(size)
                 .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName("_all")
                             )
                     )
                 .Build();
            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<ptypecollectionDto>(result);
            Elastic<ptypecollectionDto> datalist = new Elastic<ptypecollectionDto>();
            datalist = new Elastic<ptypecollectionDto>()
            {
                Took = list.took,
                TimeOut = list.timed_out,
                Shards = new Shards()
                {
                    Total = list._shards.total,
                    Failed = list._shards.failed,
                    Successful = list._shards.successful
                },
                Hits = new Hits<ptypecollectionDto>()
                {
                    MaxScore = list.hits.max_score,
                    Total = list.hits.total,
                    Data = list.hits.hits.Select(p => new ptypecollectionDto()
                    {
                        _index = p._index,
                        _type = p._type,
                        _id = p._id,
                        _score = p._score,
                        ID = p._source.ID,
                        Classify = p._source.Classify,
                        FullName = p._source.FullName,
                        Name = p._source.Name,
                        E_Name = p._source.E_Name,
                        Standard = p._source.Standard,
                        Type = p._source.Type,
                        PermitNo = p._source.PermitNo,
                        PermitNoPeriod = p._source.PermitNoPeriod,
                        CompanyName = p._source.CompanyName,
                        BarCode = p._source.BarCode,
                        Unit1 = p._source.Unit1,
                        Unit2 = p._source.Unit2,
                        Unit3 = p._source.Unit3,
                        UnitRate1 = p._source.UnitRate1,
                        Unitrate2 = p._source.Unitrate2,
                        EditDate = p._source.EditDate,
                        AddDate = p._source.AddDate,
                        AdverseReaction = p._source.AdverseReaction,
                        Address = p._source.Address,
                        ChildDrugUse = p._source.ChildDrugUse,
                        Component = p._source.Component,
                        CreateDate = p._source.CreateDate,
                        DrugReaction = p._source.DrugReaction,
                        InnerID = p._source.InnerID,
                        GMPCode = p._source.GMPCode,
                        GMPPeriod = p._source.GMPPeriod,
                        LiverKidneyDysfunction = p._source.LiverKidneyDysfunction,
                        OlderDrugUse = p._source.OlderDrugUse,
                        PregnacyDrugUse = p._source.PregnacyDrugUse,
                        ProhibitShareDrug = p._source.ProhibitShareDrug,
                        UsefulLifeDay = p._source.UsefulLifeDay,
                        ValDateType = p._source.ValDateType,
                        Indiaction = p._source.Indiaction,
                        IndicationsFunction = p._source.IndicationsFunction,
                        Ingredient = p._source.Ingredient,
                        IsCustom = p._source.IsCustom,
                        IsUse = p._source.IsUse,
                        Note = p._source.Note,
                        PharmacistPrompt = p._source.PharmacistPrompt,
                        Pharmacokinetics = p._source.Pharmacokinetics,
                        PharmacologyToxicology = p._source.PharmacologyToxicology,
                        PictureInfo = p._source.PictureInfo,
                        StorageCondition = p._source.StorageCondition,
                        Taboo = p._source.Taboo,
                        TypeStatus = p._source.TypeStatus,
                        Useage = p._source.Useage,
                        UseCount = p._source.UseCount,
                        UsefulLifeMonth = p._source.UsefulLifeMonth
                    }).ToList()
                }
            };
            return datalist;
        }


        //全文检索，单个字段或者多字段 或关系
        //字段intro 包含词组key中的任意一个单词
        //
        public personList Search(string indexName, string indexType, string key, int from, int size)
        {
            string cmd = new SearchCommand(indexName, indexType);
            string query = new QueryBuilder<person>()
                //1 查询
                .Query(b =>
                            b.Bool(m =>
                                //并且关系
                                m.Must(t =>

                                   //分词的最小单位或关系查询
                                   t.QueryString(t1 => t1.DefaultField("intro").Query(key))
                                     //.QueryString(t1 => t1.DefaultField("name").Query(key))
                                     // t .Terms(t2=>t2.Field("intro").Values("研究","方鸿渐"))
                                     //范围查询
                                     // .Range(r =>  r.Field("age").From("100").To("200") )  
                                     )
                                  )
                                )
                 //分页
                 .From(from)
                 .Size(size)
                  //排序
                  // .Sort(c => c.Field("age", SortDirection.desc))
                  //添加高亮
                  .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName("intro").Order(HighlightOrder.score),
                             f => f.FieldName("_all")
                             )
                     )
                    .Build();


            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<person>(result);
            personList datalist = new personList();
            datalist.hits = list.hits.total;
            datalist.took = list.took;
            var personList = list.hits.hits.Select(c => new person()
            {
                id = c._source.id,
                age = c._source.age,
                birthday = c._source.birthday,
                intro = string.Join("", c.highlight["intro"]), //高亮显示的内容，一条记录中出现了几次
                name = c._source.name,
                sex = c._source.sex,

            });
            datalist.list.AddRange(personList);
            return datalist;


        }

        //全文检索，多字段 并关系
        //字段intro 或者name 包含词组key
        public personList SearchFullFileds<person1>(string indexName, string indexType, string key, int from, int size)
        {
            MustQuery<person> mustNameQueryKeys = new MustQuery<person>();
            MustQuery<person> mustIntroQueryKeys = new MustQuery<person>();
            var arrKeys = GetIKTokenFromStr(key);
            foreach (var item in arrKeys)
            {
                mustNameQueryKeys = mustNameQueryKeys.Term(t3 => t3.Field("name").Value(item)) as MustQuery<person>;
                mustIntroQueryKeys = mustIntroQueryKeys.Term(t3 => t3.Field("intro").Value(item)) as MustQuery<person>;
            }

            string cmd = new SearchCommand(indexName, indexType);
            string query = new QueryBuilder<person>()
                //1 查询
                .Query(b =>
                            b.Bool(m =>
                                m.Should(t =>
                                         t.Bool(m1 =>
                                                     m1.Must(
                                                             t2 =>
                                                                 //t2.Term(t3=>t3.Field("name").Value("研究"))
                                                                 //   .Term(t3=>t3.Field("name").Value("方鸿渐"))  
                                                                 mustNameQueryKeys
                                                             )
                                                )
                                       )
                               .Should(t =>
                                         t.Bool(m1 =>
                                                     m1.Must(t2 =>
                                                                     //t2.Term(t3 => t3.Field("intro").Value("研究"))
                                                                     //.Term(t3 => t3.Field("intro").Value("方鸿渐"))  
                                                                     mustIntroQueryKeys
                                                            )
                                                )
                                      )
                                  )
                        )
                 //分页
                 .From(from)
                 .Size(size)
                  //排序
                  // .Sort(c => c.Field("age", SortDirection.desc))
                  //添加高亮
                  .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName("intro").Order(HighlightOrder.score),
                              f => f.FieldName("name").Order(HighlightOrder.score)
                             )
                     )
                    .Build();


            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<person>(result);
            personList datalist = new personList();
            datalist.hits = list.hits.total;
            datalist.took = list.took;
            var personList = list.hits.hits.Select(c => new person()
            {
                id = c._source.id,
                age = c._source.age,
                birthday = c._source.birthday,
                intro = c.highlight == null || !c.highlight.Keys.Contains("intro") ? c._source.intro : string.Join("", c.highlight["intro"]), //高亮显示的内容，一条记录中出现了几次
                name = c.highlight == null || !c.highlight.Keys.Contains("name") ? c._source.name : string.Join("", c.highlight["name"]),
                sex = c._source.sex

            });
            datalist.list.AddRange(personList);
            return datalist;


        }

        //全文检索，多字段 并关系
        //搜索age在100到200之间，并且字段intro 或者name 包含词组key
        public personList SearchFullFiledss<person>(string indexName, string indexType, string key, int from, int size)
        {
            MustQuery<DAL.person> mustNameQueryKeys = new MustQuery<DAL.person>();
            MustQuery<DAL.person> mustIntroQueryKeys = new MustQuery<DAL.person>();
            var arrKeys = GetIKTokenFromStr(key);
            foreach (var item in arrKeys)
            {
                mustNameQueryKeys = mustNameQueryKeys.Term(t3 => t3.Field("name").Value(item)) as MustQuery<DAL.person>;
                mustIntroQueryKeys = mustIntroQueryKeys.Term(t3 => t3.Field("intro").Value(item)) as MustQuery<DAL.person>;
            }

            string cmd = new SearchCommand(indexName, indexType);
            string query = new QueryBuilder<DAL.person>()
                //1 查询
                .Query(b =>
                            b.Bool(m =>
                                m.Must(t =>
                                          t.Range(r => r.Field("age").From("1").To("500"))
                                          .Bool(ms =>
                                                    ms.Should(ts =>
                                                             ts.Bool(m1 =>
                                                                         m1.Must(
                                                                                 t2 =>
                                                                                      //t2.Term(t3=>t3.Field("name").Value("研究"))
                                                                                      //   .Term(t3=>t3.Field("name").Value("方鸿渐"))  
                                                                                      //
                                                                                      mustNameQueryKeys
                                                                                 )
                                                                    )
                                                           )
                                                   .Should(ts =>
                                                             ts.Bool(m1 =>
                                                                         m1.Must(t2 =>
                                                                                        //t2.Term(t3 => t3.Field("intro").Value("研究"))
                                                                                        //.Term(t3 => t3.Field("intro").Value("方鸿渐"))  

                                                                                        //
                                                                                        mustIntroQueryKeys
                                                                                )
                                                                    )
                                                          )
                                                      )
                                                        )
                                                      )
                       )
                 //分页
                 .From(from)
                 .Size(size)
                  //排序
                  // .Sort(c => c.Field("age", SortDirection.desc))
                  //添加高亮
                  .Highlight(h => h
                      .PreTags("<b>")
                      .PostTags("</b>")
                      .Fields(
                             f => f.FieldName("intro").Order(HighlightOrder.score),
                              f => f.FieldName("name").Order(HighlightOrder.score)
                             )
                     )
                    .Build();


            string result = Client.Post(cmd, query);
            var serializer = new JsonNetSerializer();
            var list = serializer.ToSearchResult<DAL.person>(result);
            personList datalist = new personList();
            datalist.hits = list.hits.total;
            datalist.took = list.took;
            var personList = list.hits.hits.Select(c => new DAL.person()
            {
                id = c._source.id,
                age = c._source.age,
                birthday = c._source.birthday,
                intro = c.highlight == null || !c.highlight.Keys.Contains("intro") ? c._source.intro : string.Join("", c.highlight["intro"]), //高亮显示的内容，一条记录中出现了几次
                name = c.highlight == null || !c.highlight.Keys.Contains("name") ? c._source.name : string.Join("", c.highlight["name"]),
                sex = c._source.sex

            });
            datalist.list.AddRange(personList);
            return datalist;


        }
        //分词映射
        private static string BuildCompanyMapping()
        {
            return new MapBuilder<person>()
                .RootObject(typeName: "person",
                            map: r => r
                    .All(a => a.Enabled(false))
                    .Dynamic(false)
                    .Properties(pr => pr
                        .String(person => person.name, f => f.Analyzer(DefaultAnalyzers.standard).Boost(2))
                        .String(person => person.intro, f => f.Analyzer("ik"))
                        )
              )
              .BuildBeautified();
        }

        //将语句用ik分词，返回分词结果的集合
        private List<string> GetIKTokenFromStr(string key)
        {
            string s = "/db_test/_analyze?analyzer=ik";
            var result = Client.Post(s, "{" + key + "}");
            var serializer = new JsonNetSerializer();
            var list = serializer.Deserialize(result, typeof(ik)) as ik;
            return list.tokens.Select(c => c.token).ToList();
        }


    }
}
